import json
import os.path
import threading
import socket
import time
import traceback
import uuid
from concurrent.futures import ThreadPoolExecutor
from pathlib import Path

import requests


def scan_ip(ip, port):
    sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    try:
        sock.settimeout(0.1)  # 设置超时
        result = sock.connect_ex((ip, port))  # 连接到指定的IP和端口
        if result == 0:
            return ip, port
    except socket.timeout:
        pass
    finally:
        sock.close()


def broadcast_scan(ip_prefix, port):
    tasks = []
    success = []
    with ThreadPoolExecutor(max_workers=100) as executor:
        for i in range(1, 255):
            ip = ip_prefix + str(i)
            tasks.append(executor.submit(scan_ip, ip, port))

    for task in tasks:
        status = task.result()
        if status:
            success.append(status)
    if success:
        return success
    return []


def upload_file(host, port, file_path):
    url = f'http://{host}:6986/upload/'
    with open('chat_files/' + file_path, 'rb') as file:
        files = {'file': file}
        try:
            response = requests.post(url, files=files)
            if response.status_code == 200:
                return response.json()
            else:
                print('文件上传失败')
        except requests.RequestException as error:
            print(f'上传文件时出错: {error}')


class TransportClient:
    def __init__(self, ip_prefix):
        self.ip_prefix = ip_prefix
        self.servers = {}

    def start(self, port):
        def scan_online(port):
            while True:
                try:
                    print(self.ip_prefix, port)
                    servers = broadcast_scan(self.ip_prefix, port)
                    if not servers:
                        return
                    print(f"在线人数： {len(servers)}")
                    current_server_host = [x['host'] for x in self.servers.values()]
                    servers = list(filter(lambda x: x[0] not in current_server_host, servers))
                    print(f"新增在线人数： {len(servers)}")

                    for host, _ in servers:
                        user_id = uuid.uuid4().hex
                        client_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
                        client_socket.connect((host, port))  # 连接到服务端
                        self.servers[user_id] = {'socket': client_socket, 'host': host, 'port': port}

                        print("Connected to the server: ", host, port)
                        threading.Thread(target=self.receive_messages, args=(client_socket, user_id),
                                         daemon=True).start()
                except Exception as error:
                    print('error', error)
                time.sleep(10)

        threading.Thread(target=scan_online, args=(port,), daemon=True).start()

    def receive_messages(self, client_socket, user_id):
        """独立线程接收来自服务器的消息"""
        while True:
            try:
                try:
                    message = client_socket.recv(1024)
                except ConnectionResetError:
                    del self.servers[user_id]
                    break

                if not message:
                    break  # 如果客户端断开连接
                message = json.loads(message.decode())

                if message['type'] == 'file':
                    if message.get('status') == 'download':
                        user_id = message.get('user_id')
                        server = self.servers.get(user_id)
                        if server:
                            result = upload_file(server['host'], server['port'], message['filepath'])
                            if result.get('download_url'):
                                message['status'] = 'done'
                                message['download_url'] = result['download_url']
                                server['socket'].send(json.dumps(message).encode('utf-8'))
                                continue
                    else:
                        filename = message['filename']
                        now_time = str(int(time.time_ns()))
                        basedir = Path("chat_files") / now_time
                        basedir.mkdir(parents=True, exist_ok=True)

                        with open(basedir / filename, 'wb') as f:
                            remaining = message['filesize']
                            while remaining > 0:
                                chunk = client_socket.recv(min(1024, remaining))
                                f.write(chunk)
                                remaining -= len(chunk)

                        message['filepath'] = now_time + '/' + filename

                message['status'] = 'done'
                self.send_message(message)

            except Exception as e:
                traceback.print_exc()
                print(f"Error receiving message: {e}")
                del self.servers[user_id]
                break

    def send_message(self, message: dict):
        """发送消息给服务器"""
        if self.servers:
            for _id, server in self.servers.items():
                message['user_id'] = _id
                print('client send message: ', message)
                server['socket'].send(json.dumps(message).encode('utf-8'))
